﻿
using System;
using System.Collections.Generic;
using System.Linq;
using Test30.Core;

namespace Test30.Engine
{
    public abstract class StatBase
    {
        internal event EventHandler Changed;
        internal event EventHandler BonusChanged;
        public int Base
        {
            get => _base;
            set
            {
                value = value.Clamp(GetBaseMin(), GetBaseMax());
                if (!Equals(_base, value))
                {
                    _base = value;
                }
            }
        }

        public int BonusTotal => _bonuses.Values.Sum();
        public bool IsLowered => _bonuses.Values.Any((value) => value < 0);
        public bool IsRaised => _bonuses.Values.Any((value) => value > 0);

        protected StatBase(int baseValue)
        {
            _base = baseValue.Clamp(GetBaseMin(), GetBaseMax());
        }

        public void SetBonus(BonusType bonus, int value)
        {
            if (!_bonuses.ContainsKey(bonus))
                _bonuses[bonus] = 0;

            if (!Equals(_bonuses[bonus],value))
            {
                _bonuses[bonus] = value;
                OnBonusChanged();
                OnChanged();
            }
        }

        public void AddBonus(BonusType bonus, int value)
        {
            if (value == 0) return;
            if(!_bonuses.ContainsKey(bonus))
                _bonuses[bonus] = 0;

            _bonuses[bonus] += value;
            OnBonusChanged();
            OnChanged();
        }

        public bool HasBonus(BonusType bonus)
        {
            if (_bonuses.ContainsKey(bonus)) return _bonuses[bonus] != 0;
            return false;
        }

        public int GetBonus(BonusType bonus)
        {
            if (_bonuses.ContainsKey(bonus)) return _bonuses[bonus];
            return 0;
        }

        public bool Restore()
        {
            List<BonusType> toRemove = new List<BonusType>();
            foreach (KeyValuePair<BonusType, int> pair in _bonuses)
            {
                if (pair.Value < 0) toRemove.Add(pair.Key);
            }

            if (toRemove.Count > 0)
            {
                foreach (BonusType bonus in toRemove)
                {
                    _bonuses.Remove(bonus);
                }
                OnBonusChanged();
                OnChanged();
                return true;
            }
            else
            {
                return false;
            }
        }
        protected abstract int GetBaseMin();
        protected abstract int GetBaseMax();

        protected void OnChanged()
        {
            Changed?.Invoke(this, EventArgs.Empty);
        }

        #region virtual api

        protected virtual void OnBonusChanged()
        {
            BonusChanged?.Invoke(this, EventArgs.Empty);
        }
        protected virtual void OnBaseChanged()
        {
            // do nothing
        }
        #endregion

        private int _base;
        private readonly Dictionary<BonusType, int> _bonuses = new Dictionary<BonusType, int>();
    }
}
